import { getSession } from '@/lib/auth';
import { db } from '@/lib/db';
import { order, shop } from '@/lib/db/schema/common';
import type { shopStatusValues } from '@/lib/db/schema/enum';
import { gorse } from '@/lib/gorse';
import { calculateBoundingBox, result } from '@/lib/utils';
import { and, between, desc, eq, ilike, inArray, or, sql } from 'drizzle-orm';
import { BizError, ErrorCode } from '../error';

export const getShop = async () => {
  try {
    const session = await getSession();
    if (!session?.user) throw new Error('用户未登录');
    const res = await db.query.shop.findFirst({
      where: (shop, { eq }) => eq(shop.userId, session.user.id),
    });
    if (!res) throw new Error('店铺不存在');
    return result(res);
  } catch (error) {
    return result(error, '获取店铺失败');
  }
};

export const getShopById = async (id: number) => {
  try {
    const res = await db.query.shop.findFirst({
      where: (shop, { eq }) => eq(shop.id, id),
      with: {
        products: true,
      },
    });
    if (!res) throw new Error('店铺不存在');
    return result(res);
  } catch (error) {
    return result(error, '获取店铺失败');
  }
};

export const listHotShops = async () => {
  try {
    const res = await db
      .select({
        id: shop.id,
        imageKeys: shop.imageKeys,
        name: shop.name,
        orderCount: sql<number>`count(${order.id})`
          .mapWith(Number)
          .as('order_count'),
      })
      .from(shop)
      .leftJoin(order, eq(shop.id, order.shopId))
      .groupBy(shop.id)
      .where(eq(shop.status, 'normal'))
      .orderBy(desc(sql`order_count`))
      .limit(5);
    return result(res);
  } catch (error) {
    return result(error, '获取店铺失败');
  }
};

export const listNearbyShops = async ({
  page,
  pageSize,
  longitude,
  latitude,
  range = 5000,
  keywords,
}: {
  page: number;
  pageSize: number;
  longitude: number;
  latitude: number;
  range: number;
  keywords?: string;
}) => {
  try {
    const { topLeft, bottomRight } = calculateBoundingBox(
      latitude,
      longitude,
      range,
    );
    const res = await db
      .select({
        id: shop.id,
        name: shop.name,
        imageKeys: shop.imageKeys,
        description: shop.description,
        orderCount: sql<number>`count(${order.id})`
          .mapWith(Number)
          .as('order_count'),
      })
      .from(shop)
      .leftJoin(order, eq(shop.id, order.shopId))
      .where(
        and(
          eq(shop.status, 'normal'),
          between(shop.longitude, topLeft.longitude, bottomRight.longitude),
          between(shop.latitude, bottomRight.latitude, topLeft.latitude),
          keywords
            ? or(
                ilike(shop.name, `%${keywords}%`),
                ilike(shop.description, `%${keywords}%`),
              )
            : undefined,
        ),
      )
      .groupBy(shop.id)
      .orderBy(desc(sql`order_count`))
      .limit(pageSize + 1)
      .offset((page - 1) * pageSize);
    if (res.length > pageSize) {
      res.pop();
      return result({
        data: res,
        next: page + 1,
      });
    }
    return result({
      data: res,
      next: null,
    });
  } catch (error) {
    return result(error, '获取店铺失败');
  }
};

export const adminListShopsByPage = async (args: {
  page: number;
  pageSize: number;
  id?: number;
  name?: string;
  address?: string;
  contactPhone?: string;
  status?: (typeof shopStatusValues)[number][];
  dateRange?: {
    from: Date;
    to?: Date;
  };
}) => {
  try {
    const session = await getSession();
    if (session?.user.role !== 'admin')
      throw new BizError(ErrorCode.Unauthorized, '非管理员用户');
    const res = await db.query.shop.findMany({
      columns: {
        id: true,
        name: true,
        address: true,
        contactPhone: true,
        licenseKey: true,
        hygieneCertKey: true,
        status: true,
        createdAt: true,
      },
      where: and(
        args.id !== undefined ? eq(shop.id, args.id) : undefined,
        args.name !== undefined
          ? ilike(shop.name, `%${args.name}%`)
          : undefined,
        args.address !== undefined
          ? ilike(shop.address, `%${args.address}%`)
          : undefined,
        args.contactPhone !== undefined
          ? ilike(shop.contactPhone, `%${args.contactPhone}%`)
          : undefined,
        args.status !== undefined
          ? inArray(shop.status, args.status)
          : undefined,
        args.dateRange !== undefined
          ? between(
              shop.createdAt,
              args.dateRange.from,
              args.dateRange.to ? args.dateRange.to : new Date(),
            )
          : undefined,
      ),
      offset: (args.page - 1) * args.pageSize,
      limit: args.pageSize + 1,
      orderBy: desc(shop.createdAt),
    });
    if (res.length > args.pageSize) {
      res.pop();
      return result({
        data: res,
        next: args.page + 1,
      });
    }
    return result({
      data: res,
      next: null,
    });
  } catch (error) {
    return result(error, '获取店铺失败');
  }
};

export const listRecommendedShops = async ({
  userId,
  page,
  pageSize,
}: { userId: string; page: number; pageSize: number }) => {
  try {
    const recommends = await gorse.getUserRecommendations({
      userId,
      page,
      pageSize,
    });
    const shops = await db
      .select({
        id: shop.id,
        name: shop.name,
        imageKeys: shop.imageKeys,
        description: shop.description,
        orderCount: sql<number>`count(${order.id})`.mapWith(Number),
      })
      .from(shop)
      .leftJoin(order, eq(shop.id, order.shopId))
      .where(inArray(shop.id, recommends?.data?.map((i) => +i) ?? []))
      .groupBy(shop.id);
    return result({
      data: shops,
      next: recommends.next,
    });
  } catch (error) {
    return result(error, '获取推荐店铺失败');
  }
};
